package com.umeox.moto.watch.themes.skin.loader;


import static com.umeox.moto.watch.themes.AppJob.EXTRA_JOB_TYPE;
import static com.umeox.moto.watch.themes.AppJob.JOB_TYPE_SKIN_LOAD;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.Log;

import com.umeox.moto.watch.themes.BuildConfig;
import com.umeox.moto.watch.themes.R;
import com.umeox.moto.watch.themes.skin.listener.IResourceChangedListener;
import com.umeox.moto.watch.themes.util.L;
import com.umeox.moto.watch.themes.util.ThemeUtils;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;


/**
 * Skin Manager Instance
 */
public class ResourceManager {

    private List<IResourceChangedListener> skinObservers;
    private Context context;
    private String skinPackageName;
    private Resources mResources;
    private String skinPath;
    private boolean isDefaultSkin = false;
    private boolean mLoadSkinCompleted;
    private Handler mHandler;

    private ResourceManager() {
    }

    /**
     * return a global static instance of {@link ResourceManager}
     *
     * @return
     */
    public static ResourceManager getInstance() {
        return InnerHolder.instance;
    }

    /**
     * whether the skin being used is from external .skin file
     *
     * @return is external skin = true
     */
    public boolean isExternalSkin() {
        return !isDefaultSkin && mResources != null;
    }

    /**
     * get current skin path
     *
     * @return current skin path
     */
    public String getSkinPath() {
        return skinPath;
    }

    public String getSkinPackageName() {
        return skinPackageName;
    }

    public Resources getResources() {
        return mResources;
    }

    public void init(Context ctx) {
        context = ctx.getApplicationContext();
    }

    public void restoreDefaultTheme() {
        isDefaultSkin = true;
        mResources = context.getResources();
        notifyResourceChanged();
    }


    /**
     * Load resources from apk in asyc task
     *
     * @param theme path of skin apk
     */
    @SuppressLint("StaticFieldLeak")
    public void load(int theme) {
        Log.d("skin", "load theme:" + theme);
        mLoadSkinCompleted = false;
        if(mHandler == null){
            mHandler = new Handler(context.getMainLooper());
        }
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    int currentTheme = theme;
                    if (currentTheme <= 0) {
                        mResources = null;
                        mHandler.post(() -> {
                            mLoadSkinCompleted = true;
                            if (mResources != null) {
                                notifyResourceChanged();
                            } else {
                                restoreDefaultTheme();
                            }
                        });
                        return;
                    }
                    final File skinPath = ThemeUtils.getSkinPath();
                    if (skinPath == null) {
                        Log.e("skin", "ResourceManager: skin dir mk failure");
                        mResources = null;
                        mHandler.post(() -> {
                            mLoadSkinCompleted = true;
                            if (mResources != null) {
                                notifyResourceChanged();
                            } else {
                                restoreDefaultTheme();
                            }
                        });
                        return;
                    }
                    final String[] skinNames = context.getResources().getStringArray(R.array.skin_name);
                    if(currentTheme >= skinNames.length ){
                        mResources = null;
                        mHandler.post(() -> {
                            mLoadSkinCompleted = true;
                            if (mResources != null) {
                                notifyResourceChanged();
                            } else {
                                restoreDefaultTheme();
                            }
                        });
                        return;
                    }

                    File file = new File(skinPath, skinNames[currentTheme]);
                    Log.i("skin", "doInBackground: init resources " + file.getAbsolutePath());
                    if (!file.exists()) {
                        setupAppJobService();
                        Log.e("skin", "ResourceManager: skin " + file + " resource not found");
                        mResources = null;
                        mHandler.post(() -> {
                            mLoadSkinCompleted = true;
                            if (mResources != null) {
                                notifyResourceChanged();
                            } else {
                                restoreDefaultTheme();
                            }
                        });
                        return;
                    }

                    String skinPkgPath = file.getAbsolutePath();
                    PackageManager mPm = context.getPackageManager();
                    PackageInfo mInfo = mPm.getPackageArchiveInfo(skinPkgPath, PackageManager.GET_ACTIVITIES);
                    skinPackageName = mInfo.packageName;
                    AssetManager assetManager = AssetManager.class.newInstance();
                    Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
                    addAssetPath.invoke(assetManager, skinPkgPath);
                    Resources superRes = context.getResources();
                    Resources skinResource = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
                    ResourceManager.this.skinPath = skinPkgPath;
                    isDefaultSkin = false;
                    mResources = skinResource;

                } catch (Exception e) {
                    e.printStackTrace();
                }

                mHandler.post(() -> {
                    mLoadSkinCompleted = true;
                    Log.i("skin", "-------------onPostExecute Load Skin Completed------------" + mResources);
                    if (mResources != null) {
                        notifyResourceChanged();
                    } else {
                        restoreDefaultTheme();
                    }
                });
            }
        });
//        new AsyncTask<Integer, Void, Resources>() {
//
//            @Override
//            protected Resources doInBackground(Integer... params) {
//
//                try {
//                    int currentTheme = params[0];
//                    if (currentTheme <= 0) {
//                        return null;
//                    }
//                    final File skinPath = ThemeUtils.getSkinPath();
//                    if (skinPath == null) {
//                        Log.e("skin", "ResourceManager: skin dir mk failure");
//                        return null;
//                    }
//                    //TODO
//                    final String[] skinNames = context.getResources().getStringArray(R.array.skin_name);
//
//                    File file = new File(skinPath, skinNames[currentTheme]);
//                    Log.i("skin", "doInBackground: init resources " + file.getAbsolutePath());
//                    if (!file.exists()) {
//                        setupAppJobService();
//                        Log.e("skin", "ResourceManager: skin " + file + " resource not found");
//                        return null;
//                    }
//
//                    String skinPkgPath = file.getAbsolutePath();
//
//                    PackageManager mPm = context.getPackageManager();
//                    PackageInfo mInfo = mPm.getPackageArchiveInfo(skinPkgPath, PackageManager.GET_ACTIVITIES);
//                    skinPackageName = mInfo.packageName;
//
//                    AssetManager assetManager = AssetManager.class.newInstance();
//                    Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
//                    addAssetPath.invoke(assetManager, skinPkgPath);
//
//                    Resources superRes = context.getResources();
//                    Resources skinResource = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
//                    ResourceManager.this.skinPath = skinPkgPath;
//                    isDefaultSkin = false;
//                    return skinResource;
//
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    return null;
//                }
//            }
//
//            @Override
//            protected void onPostExecute(Resources result) {
//                mLoadSkinCompleted = true;
//                Log.i("skin", "-------------onPostExecute Load Skin Completed------------" + result);
//                mResources = result;
//
//                if (mResources != null) {
//                    notifyResourceChanged();
//                } else {
//                    restoreDefaultTheme();
//                }
//            }
//        }.execute(theme);
    }

    public void setupAppJobService() {
        Intent intent = new Intent();
        intent.setAction("com.umeox.watch.launcher.APP_JOB_SERVICE");
        intent.setPackage("com.umeox.watch.moto.launcher");
        intent.putExtra(EXTRA_JOB_TYPE, JOB_TYPE_SKIN_LOAD);
        context.startService(intent);
    }

    public void attach(IResourceChangedListener observer) {
        if (skinObservers == null) {
            skinObservers = new ArrayList<>();
        }
        if (!skinObservers.contains(observer)) {
            skinObservers.add(observer);
        }
    }

    public void detach(IResourceChangedListener observer) {
        if (skinObservers == null) {
            return;
        }
        skinObservers.remove(observer);
    }

    public void notifyResourceChanged() {
        if (skinObservers == null) {
            return;
        }
        for (IResourceChangedListener observer : skinObservers) {
            observer.onResourceChanged();
        }
    }

    public int getColor(int resId) {
        int originColor = context.getResources().getColor(resId);
        if (mResources == null || isDefaultSkin) {
            return originColor;
        }

        String resName = context.getResources().getResourceEntryName(resId);

        int trueResId = mResources.getIdentifier(resName, "color", skinPackageName);
        int trueColor = 0;

        try {
            trueColor = mResources.getColor(trueResId);
        } catch (NotFoundException e) {
            e.printStackTrace();
            trueColor = originColor;
        }

        return trueColor;
    }

    @SuppressLint("NewApi")
    public Drawable getDrawable(int resId) {

        Drawable originDrawable = context.getResources().getDrawable(resId);

        String resName = context.getResources().getResourceEntryName(resId);

        if (BuildConfig.DEBUG) {
            Log.i("skin ", "defaultId: " + resId + " Resources finish:" + (mResources != null) + " ResourceEntryName:" + resName);
        }

        if (resId == 0 || mResources == null || isDefaultSkin) {

            return originDrawable;
        }

        int trueResId = mResources.getIdentifier(resName, "drawable", skinPackageName);
        if (BuildConfig.DEBUG) {
            Log.d("skin", "getDrawable resName:" + resName + " defaultId:" + resId + " themeId:" + trueResId);
        }
        if (trueResId == 0) {
            return originDrawable;
        }
        Drawable trueDrawable = null;
        try {

            if (android.os.Build.VERSION.SDK_INT < 22) {
                trueDrawable = mResources.getDrawable(trueResId);
            } else {
                trueDrawable = mResources.getDrawable(trueResId, null);
            }
        } catch (Exception e) {
            if (BuildConfig.DEBUG) {
                e.printStackTrace();
            }
            trueDrawable = originDrawable;
        }

        return trueDrawable;
    }

    /**
     * 加载指定资源颜色drawable,转化为ColorStateList，保证selector类型的Color也能被转换。</br>
     * 无皮肤包资源返回默认主题颜色
     *
     * @param resId
     * @return
     * @author pinotao
     */
    public ColorStateList convertToColorStateList(int resId) {
        L.e("attr1", "convertToColorStateList");

        boolean isExtendSkin = true;

        if (mResources == null || isDefaultSkin) {
            isExtendSkin = false;
        }

        String resName = context.getResources().getResourceEntryName(resId);
//        L.e("attr1", "resName = " + resName);
        if (isExtendSkin) {
//            L.e("attr1", "isExtendSkin");
            int trueResId = mResources.getIdentifier(resName, "color", skinPackageName);
//            L.e("attr1", "trueResId = " + trueResId);
            ColorStateList trueColorList = null;
            if (trueResId == 0) { // 如果皮肤包没有复写该资源，但是需要判断是否是ColorStateList
                try {
                    ColorStateList originColorList = context.getResources().getColorStateList(resId);
                    return originColorList;
                } catch (NotFoundException e) {
                    e.printStackTrace();
                    L.e("resName = " + resName + " NotFoundException : " + e.getMessage());
                }
            } else {
                try {
                    trueColorList = mResources.getColorStateList(trueResId);
                    L.e("attr1", "trueColorList = " + trueColorList);
                    return trueColorList;
                } catch (NotFoundException e) {
                    e.printStackTrace();
                    L.w("resName = " + resName + " NotFoundException :" + e.getMessage());
                }
            }
        } else {
            try {
                return context.getResources().getColorStateList(resId);
            } catch (NotFoundException e) {
                e.printStackTrace();
                L.w("resName = " + resName + " NotFoundException :" + e.getMessage());
            }

        }

        int[][] states = new int[1][1];
        return new ColorStateList(states, new int[]{context.getResources().getColor(resId)});
    }

    public boolean isLoadSkinCompleted() {
        return mLoadSkinCompleted;
    }

    public void setLoadSkinCompleted(boolean loadSkinCompleted) {
        this.mLoadSkinCompleted = loadSkinCompleted;
    }

    @SuppressLint("NewApi")
    public Bitmap getBitmap(int resId) {
        final Resources resources = context.getResources();
        if (resId == 0 || mResources == null || isDefaultSkin) {
            return BitmapFactory.decodeResource(resources, resId);
        }

        String resName = resources.getResourceEntryName(resId);
        int trueResId = mResources.getIdentifier(resName, "drawable", skinPackageName);

        final boolean isNull = trueResId == 0;
        return BitmapFactory.decodeResource(isNull ? resources : mResources, isNull ? resId : trueResId);
    }

    private int getDrawableId(String name) {
        return getResources().getIdentifier(name, "drawable", context.getPackageName());
    }

    private static class InnerHolder {
        static ResourceManager instance = new ResourceManager();
    }
}